﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AuthApi.Contracts;
using System.DirectoryServices;
using Web.Framework;
using ApiAuth.Data;
using System.Transactions;
using AuthApi.Contracts.Search;
using Web.Framework.Mapper;

namespace ApiAuth.Services
{
    public class AuthApiManagementService : IAuthApiManagement
    {
        public List<WebApplicationInfo> GetApplications(string nameLike)
        {
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                IQueryable<DbApplication> query = ctx.DbApplications;
                if (!String.IsNullOrEmpty(nameLike))
                {
                    query = query.Where(app => app.Name.ToLower().Contains(nameLike.ToLower()));
                }

                var queryWebApp = query.Select(a =>
                        new WebApplicationInfo()
                        {
                            Id = a.Id,
                            Name = a.Name,
                            Url = a.Url,
                            RequiereLoginSinEmpresa = a.RequiereLoginSinEmpresa
                        });

                return queryWebApp.ToList();
            }
        }

        public OperationIncluded GetOperationById(int operationId)
        {
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                return ctx.DbOperations.Where(o => o.Id == operationId).Select(o => new OperationIncluded()
                {
                    Id = o.Id,
                    Name = o.Name,
                    Description = o.Description,
                    ApplicationId = o.ApplicationId
                }).Single();
            }
        }

        public OperationIncluded GetOperation(SearchOperation search)
        {
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                var dbOperation = ctx.DbOperations.Where(o => o.Id == search.Id.Value).Single();

                OperationIncluded operation = new OperationIncluded();
                operation.Id = dbOperation.Id;
                operation.Name = dbOperation.Name;
                operation.Description = dbOperation.Description;
                operation.ApplicationId = dbOperation.ApplicationId;
                operation.ApplicationName = dbOperation.Application.Name;

                if (search.LoadRelatedRoles)
                {
                    var dbRoles = dbOperation.Roles.ToList();
                    foreach (var dbRole in dbRoles)
                    {
                        RoleIncluded roleIncluded = new RoleIncluded();
                        roleIncluded.Id = dbRole.Id;
                        roleIncluded.Name = dbRole.Name;
                        roleIncluded.ApplicationId = dbRole.ApplicationId;
                        operation.RoleIncluded.Add(roleIncluded);
                        //operation.RoleIncluded
                    }
                }

                return operation;
            }
        }

        public WebApplicationInfo GetApplicationById(int applicationId)
        {
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                //return ctx.DbApplications.Where(app => app.Id == applicationId).Select
                //    (a =>
                //        new WebApplicationInfo()
                //        {
                //            Id = a.Id,
                //            Name = a.Name,
                //            Url = a.Url,
                //            Roles = a.Roles.Select(rol => new RoleIncluded() { Id = rol.Id, ApplicationId = a.Id, Name = rol.Name, IsIncluded = true }).ToList(),
                //            Operations = a.Operations.Select(op => new OperationIncluded() { Id = op.Id, Name = op.Name, IsIncluded = true }).ToList()
                //        }).SingleOrDefault();

                DbApplication dbApp = ctx.DbApplications.Where(app => app.Id == applicationId).Single();

                WebApplicationInfo webApp = new WebApplicationInfo();
                EntityMapper.Map(dbApp, webApp, "Roles", "Operations");

                webApp.Roles = dbApp.Roles.Select(rol => new RoleIncluded() { Id = rol.Id, ApplicationId = webApp.Id, Name = rol.Name, IsIncluded = true }).ToList();
                webApp.Operations = dbApp.Operations.Select(op => new OperationIncluded() { Id = op.Id, Name = op.Name, Description = op.Description, IsIncluded = true }).ToList();

                return webApp;
            }
        }

        public void DeleteApplication(int applicationId)
        {
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                DbApplication app = ctx.DbApplications.Where(a => a.Id == applicationId).Single();

                StringBuilder errorBuilder = new StringBuilder();
                if (app.Roles.Count > 0)
                {
                    errorBuilder.AppendLine("La aplicación contiene roles, debe borrarlos primero.");
                }

                if (app.Operations.Count > 0)
                {
                    errorBuilder.AppendLine("La aplicación contiene operaciones, debe borrarlas primero.");
                }

                if (errorBuilder.Length > 0)
                {
                    throw new Exception(errorBuilder.ToString());
                }

                ctx.DbApplications.DeleteObject(app);
                ctx.SaveChanges();
            }
        }

        public WebApplicationInfo SaveApplication(WebApplicationInfo applicationInfo)
        {
            // TODO, generar una nueva operación para ver la aplicación, si es nueva
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                DbApplication dbApplication = ctx.DbApplications.Where(a => a.Id == applicationInfo.Id).SingleOrDefault();

                if (dbApplication == null)
                {
                    dbApplication = new DbApplication();
                    ctx.DbApplications.AddObject(dbApplication);
                    dbApplication.Id = applicationInfo.Id;
                }

                dbApplication.Name = applicationInfo.Name;
                dbApplication.Contacto = applicationInfo.Contacto;
                dbApplication.Url = applicationInfo.Url;

                if (!String.IsNullOrEmpty(applicationInfo.AdPassword))
                {
                    dbApplication.AdPassword = ApiCommon.Encrypt(applicationInfo.AdPassword);
                }


                ctx.SaveChanges();

                applicationInfo.Id = dbApplication.Id;

                return applicationInfo;
            }
        }

        public Company GetCompanyWithApplicationsById(int companyId)
        {
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                DbCompany dbCompany = ctx.DbCompanies.Where(c => c.Id == companyId).SingleOrDefault();

                Company company = new Company();
                company.Id = companyId;

                if (dbCompany != null)
                {
                    company.Name = dbCompany.Name;
                    company.Domain = dbCompany.Domain;
                    company.AdPath = dbCompany.AdPath;
                    company.AdUser = dbCompany.AdUser;
                    company.Applications = ctx.DbApplications.Select(a => new WebApplicationInfoIncluded() { Id = a.Id, Name = a.Name, IsIncluded = false }).ToList();

                    foreach (var compAp in dbCompany.CompanyApplications)
                    {
                        if (compAp.Application.RequiereLoginSinEmpresa)
                        {
                            var webAp = company.Applications.Where(ca => ca.Id == compAp.ApplicationId).Single();
                            company.Applications.Remove(webAp);
                        }
                        else
                        {
                            var webAp = company.Applications.Where(ca => ca.Id == compAp.ApplicationId).Single();
                            webAp.IsIncluded = true;
                        }
                    }
                }

                return company;
            }
        }

        public void DeleteRoleForApplication(int applicationId, int roleId)
        {
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                DbRole role = ctx.DbRoles.Where(r => r.Id == roleId && r.ApplicationId == applicationId).Single();

                StringBuilder errorBuilder = new StringBuilder();
                if (role.CompanyApplicationUsers.Count > 0)
                {
                    errorBuilder.AppendLine("El rol tiene usuarios relacionados, debe eliminar las relaciones primero.");
                }

                if (errorBuilder.Length > 0)
                {
                    throw new Exception(errorBuilder.ToString());
                }

                // vacio las relaciones
                //role.CompanyApplicationUsers.Clear();
                role.Operations.Clear();

                ctx.DbRoles.DeleteObject(role);
                ctx.SaveChanges();
            }
        }

        public UserWithApplication GetUserWithApplications(int companyId, string userName)
        {
            UserWithApplication userWithApplication = new UserWithApplication();
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                var usersDbAppList = (from u in ctx.DbCompanyApplicationUsers
                                      where u.AppUser.UserName == userName
                                      && (u.CompanyId == companyId || (companyId == 0 && !u.CompanyId.HasValue))
                                      select u).ToList();

                var dbApplicationList = ctx.DbApplications.ToList();

                if (companyId == 0)
                {
                    dbApplicationList = dbApplicationList.Where(a => a.RequiereLoginSinEmpresa).ToList();
                }
                else
                {
                    dbApplicationList = dbApplicationList.Where(a => !a.RequiereLoginSinEmpresa).ToList();
                }

                if (usersDbAppList.Count > 0)
                {
                    var dbUser = usersDbAppList.First();
                    userWithApplication.UserName = dbUser.AppUser.UserName;
                    userWithApplication.GivenName = dbUser.AppUser.GivenName;
                    userWithApplication.UsaPasswordLocal = !String.IsNullOrEmpty(dbUser.AppUser.Password);
                }
                else
                {
                    userWithApplication.UserName = userName;
                    // lo obtengo de AD
                    //var adUserList = GetAdUsers(userName, ctx.DbCompanies.Where(c => c.Id == companyId).Single());
                    //if (adUserList.Count > 0)
                    //{
                    //    userWithApplication.GivenName = adUserList.First().GivenName;
                    //}
                }

                foreach (var dbApp in dbApplicationList)
                {
                    var dbUserApp = usersDbAppList.Where(uapp => uapp.ApplicationId == dbApp.Id).SingleOrDefault();

                    if (String.IsNullOrEmpty(userWithApplication.GivenName))
                    {
                        userWithApplication.GivenName = IntentarObtenerGivenName(dbApp, ctx.DbCompanies.Where(c => c.Id == companyId).SingleOrDefault(), userName);
                    }

                    WebApplicationInfoIncluded appIncluded = new WebApplicationInfoIncluded();
                    appIncluded.Id = dbApp.Id;
                    appIncluded.Name = dbApp.Name;

                    if (dbUserApp != null)
                    {
                        appIncluded.IsIncluded = true;
                        appIncluded.HasPassword = !String.IsNullOrEmpty(dbUserApp.AppUser.Password);
                        appIncluded.IsUserBlocked = dbUserApp.Blocked;
                    }

                    userWithApplication.WebApplicationList.Add(appIncluded);
                }

                return userWithApplication;
            }
        }

        private string IntentarObtenerGivenName(DbApplication dbApplication, DbCompany dbCompany, string userName)
        {
            string returnName = string.Empty;
            string adPath = string.Empty;
            string adUser = string.Empty;
            string adPassword = string.Empty;

            if (dbApplication.RequiereLoginSinEmpresa)
            {
                adPath = dbApplication.AdPath;
                adUser = dbApplication.AdUser;
                adPassword = dbApplication.AdPassword;
            }
            else
            {
                adPath = dbCompany.AdPath;
                adUser = dbCompany.AdUser;
                adPassword = dbCompany.AdPassword;
            }

            // lo obtengo de AD
            var adUserList = GetAdUsers(userName, adPath, adUser, adPassword);
            if (adUserList.Count > 0)
            {
                returnName = adUserList.First().GivenName;
            }

            return returnName;
        }

        public List<RoleIncluded> GetRolesForApplication(int applicationId)
        {
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                return ctx.DbApplications.Where(a => a.Id == applicationId).Single().Roles.Select(r => new RoleIncluded() { Id = r.Id, Name = r.Name, ApplicationId = applicationId }).ToList();
            }
        }

        public bool ExistCompanyApplication(int companyId, int applicationId)
        {
            bool existe = false;
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                var dbApplication = ctx.DbApplications.Where(ap => ap.Id == applicationId).SingleOrDefault();
                if (dbApplication != null)
                {
                    if (dbApplication.RequiereLoginSinEmpresa && companyId == 0)
                    {
                        existe = true;
                    }
                    else
                    {
                        existe = dbApplication.CompanyApplications.Where(ca => ca.ApplicationId == applicationId && ca.CompanyId == companyId).Count() > 0;
                    }
                }
            }

            return existe;
        }

        public AdUser GetUser(int applicationId, int companyId, string userName)
        {
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                DbCompanyApplicationUser dbUser = ctx.DbCompanyApplicationUsers.Where(u => u.ApplicationId == applicationId &&
                                                         (u.CompanyId == companyId || (!u.CompanyId.HasValue && companyId == 0)) &&
                                                         u.AppUser.UserName == userName).SingleOrDefault();

                DbApplication dbApplication = ctx.DbApplications.Where(a => a.Id == applicationId).SingleOrDefault();

                if (dbApplication == null)
                {
                    throw new Exception("No se ha especificado una aplicación para obtener este usuario");
                }

                if (dbUser == null)
                {
                    var dbAppUser = ctx.DbAppUsers.Where(u => u.UserName == userName).SingleOrDefault();

                    if (dbAppUser == null)
                    {
                        dbAppUser = new DbAppUser();
                        ctx.DbAppUsers.AddObject(dbAppUser);
                        dbAppUser.UserName = userName;
                        dbAppUser.Blocked = false;
                        dbAppUser.AuditFechaCreacion = DateTime.Now;
                        dbAppUser.AuditUserNameCreacion = String.Empty;
                    }

                    // es de AD, entonces lo agrego a la base
                    // Validacion
                    if (dbApplication.RequiereLoginSinEmpresa && companyId != 0)
                    {
                        throw new Exception("La marca requiere un login sin marca y se ha especificado la marca");
                    }

                    dbUser = new DbCompanyApplicationUser();
                    dbUser.CompanyId = companyId == 0 ? (int?)null : companyId;
                    dbUser.ApplicationId = applicationId;
                    dbAppUser.CompanyApplicationUsers.Add(dbUser);
                    ctx.SaveChanges();
                }


                AdUser adUser = new AdUser();
                adUser.Id = dbUser.AppUser.Id;
                adUser.UserName = dbUser.AppUser.UserName;
                adUser.GivenName = dbUser.AppUser.GivenName;
                adUser.UsuarioEnAplicacion = true;
                adUser.ApplicationId = dbUser.ApplicationId;
                adUser.CompanyId = dbUser.CompanyId ?? 0;
                adUser.Blocked = dbUser.Blocked;
                adUser.BlockedDate = dbUser.BlockedDate;
                adUser.UnBlockedDate = dbUser.UnblockedDate;
                adUser.HasPassword = !String.IsNullOrEmpty(dbUser.AppUser.Password);

                adUser.Roles = dbApplication.Roles.Select(r => new RoleIncluded() { Id = r.Id, Name = r.Name, ApplicationId = dbApplication.Id }).ToList();
                var dbUserRoles = dbUser.Roles.ToList();
                foreach (var dbUserRole in dbUserRoles)
                {
                    var adRole = adUser.Roles.Where(r => r.Id == dbUserRole.Id).Single();
                    adRole.IsIncluded = true;
                }

                adUser.Operations = dbApplication.Operations.Select(o => new OperationIncluded() { Id = o.Id, Name = o.Name, Description = o.Description }).ToList();
                var dbUserOperations = dbUser.Operations.ToList();
                foreach (var dbUserOperation in dbUserOperations)
                {
                    var adOperation = adUser.Operations.Where(o => o.Id == dbUserOperation.Id).Single();
                    adOperation.IsIncluded = true;
                }

                return adUser;
            }
        }

        public List<AdUser> GetUsers(SearchUser search)
        {
            List<AdUser> adUsers = new List<AdUser>();

            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                DbCompany dbCompany = ctx.DbCompanies.Where(c => c.Id == search.CompanyId).SingleOrDefault();
                DbApplication dbApplication = ctx.DbApplications.Where(a => a.Id == search.ApplicationId).SingleOrDefault();

                if (dbCompany == null && dbApplication == null)
                {
                    throw new Exception("No se puede buscar usuarios para una marca no seleccionada sin elegir una aplicación");
                }

                //Company companyObj = new Company()
                //{
                //    AdPath = @"LDAP://vesuvio.argentina/DC=vesuvio,DC=argentina",
                //    Domain = "vesuvio",
                //    Name = "Lacoste"
                //};

                try
                {
                    //adUsers.Add(new AdUser() { UserName = "Pepe" });
                    //adUsers.Add(new AdUser() { UserName = "Juan" });
                    //adUsers.Add(new AdUser() { UserName = "Pedro" });

                    string returnName = string.Empty;
                    string adPath = string.Empty;
                    string adUser = string.Empty;
                    string adPassword = string.Empty;

                    if (dbApplication != null && dbApplication.RequiereLoginSinEmpresa)
                    {
                        adPath = dbApplication.AdPath;
                        adUser = dbApplication.AdUser;
                        adPassword = dbApplication.AdPassword;
                    }
                    else
                    {
                        adPath = dbCompany.AdPath;
                        adUser = dbCompany.AdUser;
                        adPassword = dbCompany.AdPassword;
                    }

                    adUsers.AddRange(GetAdUsers(search.NameLike, adPath, adUser, adPassword));
                }
                catch (Exception ex)
                {
                    Exception ex1 = new Exception("Error en el listado de usuarios: " + ex.Message, ex);
                    throw ex1;
                }

                // agrego los que estan en base de datos

                // Uso el objeto aduser para unificar pero en realidad es como un db user
                List<AdUser> dbUsers = new List<AdUser>();
                if (search.ApplicationId != 0)
                {
                    // Cargo los usuarios de una sola aplicacion, sin problemas
                    IQueryable<DbCompanyApplicationUser> query = ctx.DbCompanyApplicationUsers.Where(
                        cau => cau.ApplicationId == search.ApplicationId && (cau.CompanyId == search.CompanyId || (!cau.CompanyId.HasValue && search.CompanyId == 0)));
                    if (search.Bloqueados.HasValue)
                    {
                        query = query.Where(u => u.Blocked == search.Bloqueados.Value);
                    }

                    if (!String.IsNullOrEmpty(search.NameLike))
                    {
                        query = query.Where(u => u.AppUser.UserName.Contains(search.NameLike));
                    }

                    dbUsers.AddRange(query.Select(u =>
                        new AdUser()
                        {
                            UserName = u.AppUser.UserName,
                            UsuarioEnAplicacion = true,
                            GivenName = u.AppUser.GivenName,
                            Blocked = u.Blocked
                        }));
                }
                else
                {
                    IQueryable<DbAppUser> query = ctx.DbAppUsers;
                    if (search.Bloqueados.HasValue)
                    {
                        query = query.Where(u => u.Blocked == search.Bloqueados.Value);
                    }

                    if (!String.IsNullOrEmpty(search.NameLike))
                    {
                        query = query.Where(u => u.UserName.Contains(search.NameLike));
                    }

                    var dbUsersGroup = query.ToList();
                    foreach (var dbUserGroup in dbUsersGroup)
                    {
                        dbUsers.Add(new AdUser()
                            {
                                UsuarioEnAplicacion = false,
                                GivenName = dbUserGroup.GivenName,
                                UserName = dbUserGroup.UserName,
                                Password = dbUserGroup.Password,
                                Blocked = dbUserGroup.Blocked
                            });
                    }
                }

                // Si no esta buscando todos, es decir, sin especificar bloqueos, los extra de ad no van.
                if (search.Bloqueados.HasValue || search.ConsiderarRegistradosParaLaAplicacion)
                {
                    List<string> userNamesDb = dbUsers.Select(u => u.UserName).ToList();
                    List<string> userNamesAd = adUsers.Select(u => u.UserName).ToList();
                    List<string> userNamesQueNoVan = userNamesAd.Where(adu => !userNamesDb.Contains(adu)).ToList();

                    foreach (string userNameQueNoVa in userNamesQueNoVan)
                    {
                        var adToRemove = adUsers.Where(u => u.UserName == userNameQueNoVa).Single();
                        adUsers.Remove(adToRemove);
                    }
                }

                foreach (var dbUser in dbUsers)
                {
                    var adUser = adUsers.Where(ad => ad.UserName.ToLower() == dbUser.UserName.ToLower()).SingleOrDefault();
                    if (adUser != null)
                    {
                        // Esta en la base y en AD
                        adUser.UsuarioEnAplicacion = true;
                        adUser.HasPassword = !String.IsNullOrEmpty(dbUser.Password);
                        adUser.ApplicationId = search.ApplicationId;
                        adUser.CompanyId = search.CompanyId;
                        adUser.Blocked = dbUser.Blocked;
                    }
                    else
                    {
                        // El usuario no está en AD pero si en la BD, por lo tanto lo agrego a la lista (ya que es un usuario que autentica por aplicacion
                        adUser = new AdUser()
                                {
                                    UsuarioEnAplicacion = true,
                                    HasPassword = !String.IsNullOrEmpty(dbUser.Password),
                                    UserName = dbUser.UserName,
                                    GivenName = dbUser.GivenName,
                                    ApplicationId = search.ApplicationId,
                                    CompanyId = search.CompanyId,
                                    Blocked = dbUser.Blocked
                                };

                        adUsers.Add(adUser);
                    }
                }

                return adUsers;
            }
        }

        private static List<AdUser> GetAdUsers(string nameLike, string adPath, string adUser, string adPassword)
        {
            List<AdUser> adUsers = new List<AdUser>();
            DirectoryEntry entry = new DirectoryEntry(adPath, adUser, ApiCommon.Decrypt(adPassword));
            Object obj = entry.NativeObject;
            DirectorySearcher search = new DirectorySearcher(entry);

            if (String.IsNullOrEmpty(nameLike))
            {
                search.Filter = "(&(objectCategory=user)(!userAccountControl:1.2.840.113556.1.4.803:=2)(samaccountname=*)(cn=*))";
            }
            else
            {
                search.Filter = "(&(objectCategory=user)(!userAccountControl:1.2.840.113556.1.4.803:=2)(|(cn=*" + nameLike.Trim() + "*)((samaccountname=*" + nameLike.Trim() + "*))))";
            }

            search.PropertiesToLoad.Add("samaccountname");
            search.PropertiesToLoad.Add("cn");
            search.PropertiesToLoad.Add("dialin");
            SearchResultCollection results = search.FindAll();

            foreach (SearchResult result in results)
            {
                if (result.Properties.Contains("samaccountname"))
                {
                    ResultPropertyValueCollection tmpCollection = result.Properties["samaccountname"];
                    ResultPropertyValueCollection tmpCollection2 = result.Properties["cn"];
                    adUsers.Add(
                        new AdUser()
                        {
                            UserName = Convert.ToString(tmpCollection[0]),
                            GivenName = Convert.ToString(tmpCollection2[0]),
                        }
                        );
                }
            }

            return adUsers;
        }

        public bool ChangePassword(AdUser user, string oldPassword, string newPassword)
        {
            bool couldChange = false;
            bool oldPasswordOK = false;
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                DbAppUser dbUser = ctx.DbAppUsers.Where(c => c.Id == user.Id).SingleOrDefault();
                if (dbUser != null)
                {
                    if (!String.IsNullOrEmpty(dbUser.Password))
                    {
                        // Controlo el passoword anterior
                        oldPasswordOK = dbUser.Password == ApiCommon.HashPassword(oldPassword, user.UserName);
                        if (!oldPasswordOK)
                        {
                            throw new Exception("El password anterior es incorrecto");
                        }
                    }

                    if (oldPasswordOK)
                    {
                        dbUser.Password = ApiCommon.HashPassword(newPassword, user.UserName);
                        couldChange = true;
                    }

                    ctx.SaveChanges();
                }
            }

            return couldChange;
        }

        public void ResetPassword(AdUser user)
        {
            //TODO: guardar usuario, operaciones y roles y auditar
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                DbAppUser dbUser = ctx.DbAppUsers.Where(c => c.Id == user.Id).SingleOrDefault();
                if (dbUser != null)
                {
                    dbUser.Password = string.Empty;
                    ctx.SaveChanges();
                }
            }
        }

        public UserWithApplication CreateUser(UserWithApplication user)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (ApiAuthEntities ctx = new ApiAuthEntities())
                {
                    // Me fijo que no exista
                    DbAppUser dbAppUser = ctx.DbAppUsers.Where(u => u.UserName == user.UserName).SingleOrDefault();
                    if (dbAppUser != null)
                    {
                        user.UsuarioExistente = true;
                        return user;
                    }
                    else
                    {

                        dbAppUser = new DbAppUser();
                        ctx.DbAppUsers.AddObject(dbAppUser);
                        dbAppUser.UserName = user.UserName.ToLower();
                        dbAppUser.GivenName = user.GivenName;
                        dbAppUser.AuditFechaCreacion = DateTime.Now;
                        dbAppUser.AuditUserNameCreacion = string.Empty;


                        if (!String.IsNullOrEmpty(user.Password))
                        {
                            dbAppUser.Password = ApiCommon.HashPassword(user.Password, user.UserName);
                        }

                        foreach (var application in user.WebApplicationList)
                        {
                            if (application.IsIncluded)
                            {
                                DbCompanyApplicationUser dbCAUser = new DbCompanyApplicationUser();
                                dbCAUser.ApplicationId = application.Id;
                                dbCAUser.CompanyId = application.CompanyId == 0 ? (int?)null : application.CompanyId;
                                dbAppUser.CompanyApplicationUsers.Add(dbCAUser);
                            }
                        }

                        ctx.SaveChanges();
                        tScope.Complete();
                        user.Id = dbAppUser.Id;
                    }
                }
            }

            return user;
        }

        public void BloquearUsuario(int companyId, int applicationId, string userName, bool bloquear)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (ApiAuthEntities ctx = new ApiAuthEntities())
                {
                    var dbAppUser = ctx.DbAppUsers.Where(u => u.UserName == userName).SingleOrDefault();
                    if (dbAppUser != null)
                    {
                        var dbCAUser = dbAppUser.CompanyApplicationUsers.Where(c => c.ApplicationId == applicationId && 
                             ( c.CompanyId == companyId || (companyId == 0 && !c.CompanyId.HasValue))
                            ).SingleOrDefault();

                        if (dbCAUser != null)
                        {
                            dbCAUser.Blocked = bloquear;
                            dbCAUser.BlockedDate = DateTime.Now;
                        }
                    }

                    ctx.SaveChanges();
                    tScope.Complete();
                }
            }
        }

        public void IncluirUsuarioEnAplicacion(int companyId, int applicationId, string userName)
        {
            using (TransactionScope tScope = new TransactionScope())
            {
                using (ApiAuthEntities ctx = new ApiAuthEntities())
                {
                    var dbAppUser = ctx.DbAppUsers.Where(u => u.UserName == userName).SingleOrDefault();
                    if (dbAppUser != null)
                    {
                        var dbCAUser = dbAppUser.CompanyApplicationUsers.Where(c => c.ApplicationId == applicationId && (c.CompanyId == companyId || (!c.CompanyId.HasValue && companyId == 0))).SingleOrDefault();

                        if (dbCAUser == null)
                        {
                            dbCAUser = new DbCompanyApplicationUser();
                            dbCAUser.CompanyId = companyId == 0 ? (int?)null: companyId;
                            dbCAUser.ApplicationId = applicationId;
                            dbAppUser.CompanyApplicationUsers.Add(dbCAUser);
                        }
                    }

                    ctx.SaveChanges();
                    tScope.Complete();
                }
            }
        }


        public AdUser SaveAppUser(AdUser adUser)
        {
            //TODO: guardar usuario, operaciones y roles y auditar    
            using (TransactionScope tScope = new TransactionScope())
            {
                using (ApiAuthEntities ctx = new ApiAuthEntities())
                {
                    DbAppUser dbAppUser = ctx.DbAppUsers.Where(u => u.UserName == adUser.UserName).SingleOrDefault();

                    if (dbAppUser != null)
                    {
                        dbAppUser.GivenName = adUser.GivenName;
                    }

                    ctx.SaveChanges();
                    tScope.Complete();
                }
            }

            return adUser;
        }

        public AdUser SaveUser(AdUser adUser)
        {
            //TODO: guardar usuario, operaciones y roles y auditar    
            using (TransactionScope tScope = new TransactionScope())
            {
                using (ApiAuthEntities ctx = new ApiAuthEntities())
                {
                    DbAppUser dbUser = ctx.DbAppUsers.Where(u => u.UserName == adUser.UserName).SingleOrDefault();

                    if (dbUser == null)
                    {
                        dbUser = new DbAppUser();
                        ctx.DbAppUsers.AddObject(dbUser);
                        dbUser.UserName = adUser.UserName;
                        dbUser.Blocked = false;
                        dbUser.AuditFechaCreacion = DateTime.Now;
                        dbUser.AuditUserNameCreacion = String.Empty;
                    }

                    if (dbUser.GivenName != adUser.GivenName)
                    {
                        dbUser.GivenName = adUser.GivenName;
                    }

                    DbCompanyApplicationUser dbCAUser = ctx.DbCompanyApplicationUsers.Where(dbUserQ => (dbUserQ.CompanyId == adUser.CompanyId ||(!dbUserQ.CompanyId.HasValue && adUser.CompanyId == 0)) &&
                                                                                         dbUserQ.ApplicationId == adUser.ApplicationId &&
                                                                                         dbUserQ.AppUser.UserName.ToLower() == adUser.UserName.ToLower()).SingleOrDefault();
                    bool? previousBlockedState = null;
                    if (dbCAUser == null)
                    {
                        dbCAUser = new DbCompanyApplicationUser();
                        dbUser.CompanyApplicationUsers.Add(dbCAUser);
                        dbCAUser.CompanyId = adUser.CompanyId == 0 ? (int?)null: adUser.CompanyId;
                        dbCAUser.ApplicationId = adUser.ApplicationId;
                    }
                    else
                    {
                        previousBlockedState = dbCAUser.Blocked;
                    }

                    if (!String.IsNullOrEmpty(adUser.Password))
                    {
                        dbUser.Password = ApiCommon.HashPassword(adUser.Password, adUser.UserName);
                        dbUser.UserName = adUser.UserName;
                        adUser.HasPassword = true;
                    }

                    dbCAUser.Blocked = adUser.Blocked;

                    if (previousBlockedState.HasValue)
                    {
                        if (previousBlockedState != dbCAUser.Blocked)
                        {
                            if (dbCAUser.Blocked)
                            {
                                dbCAUser.BlockedDate = DateTime.Now;
                            }
                            else
                            {
                                dbCAUser.UnblockedDate = DateTime.Now;
                            }
                        }
                    }

                    // Operaciones
                    //dbUser.Operations.Load();
                    foreach (var op in adUser.Operations)
                    {
                        var dbOp = dbCAUser.Operations.Where(o => o.Id == op.Id).SingleOrDefault();
                        // tiene que estar incluido y no esta en la base, lo agrego
                        if (op.IsIncluded && dbOp == null)
                        {
                            dbCAUser.Operations.Add(ctx.DbOperations.Where(o => o.Id == op.Id).Single());
                        }
                        else if (!op.IsIncluded && dbOp != null)
                        {
                            // lo borro si no tiene que estar
                            dbCAUser.Operations.Remove(dbOp);
                        }
                    }

                    // Roles
                    //dbUser.Roles.Load();
                    foreach (var ro in adUser.Roles)
                    {
                        var dbRole = dbCAUser.Roles.Where(r => r.Id == ro.Id).SingleOrDefault();
                        if (ro.IsIncluded && dbRole == null)
                        {
                            dbCAUser.Roles.Add(ctx.DbRoles.Where(r => r.Id == ro.Id).Single());
                        }
                        else if (!ro.IsIncluded && dbRole != null)
                        {
                            dbCAUser.Roles.Remove(dbRole);
                        }
                    }

                    ctx.SaveChanges();
                    tScope.Complete();
                    adUser.Id = dbUser.Id;
                    adUser.UsuarioEnAplicacion = true;
                }
            }

            return adUser;
        }

        public List<Company> GetCompanies(string nameLike)
        {
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                IQueryable<DbCompany> query = ctx.DbCompanies;

                query = query.Where(c => c.Name.ToLower() != "outlet");

                List<Company> companies = query.Where(c => c.Name.Contains(nameLike)).Select(c => new Company()
                {
                    Id = c.Id,
                    Name = c.Name,
                    Domain = c.Domain,
                    AdPath = c.AdPath,
                    AdUser = c.AdUser
                }).ToList();

                return companies;
            }
        }

        public Company SaveCompany(Company company)
        {
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                DbCompany dbCompany = ctx.DbCompanies.Where(c => c.Id == company.Id).SingleOrDefault();
                if (dbCompany == null && company.Id != 0)
                {
                    dbCompany = new DbCompany();
                    ctx.DbCompanies.AddObject(dbCompany);
                    dbCompany.Id = company.Id;
                }
                else
                {
                    // Valido si elimina la aplicación, que no tenga usuarios
                    StringBuilder errorBuilder = new StringBuilder();
                    foreach (var appIncluded in company.Applications)
                    {
                        if (!appIncluded.IsIncluded)
                        {
                            var dbCompanyApplication = dbCompany.CompanyApplications.Where(c => c.ApplicationId == appIncluded.Id).SingleOrDefault();
                            // Si la aplicaciones esta siendo eliminada pero tiene usuarios
                            if (dbCompanyApplication != null)
                            {
                                if (dbCompanyApplication.CompanyApplicationUsers.Count > 0)
                                {
                                    errorBuilder.AppendFormat("La aplicación {0} tiene usuarios asignados no se puede desasociar.", appIncluded.Name);
                                }
                            }
                        }
                    }

                    if (errorBuilder.Length > 0)
                    {
                        throw new Exception(errorBuilder.ToString());
                    }
                }

                dbCompany.Name = company.Name;
                dbCompany.Domain = company.Domain;
                dbCompany.AdPath = company.AdPath;

                if (!String.IsNullOrEmpty(company.AdPassword))
                {
                    dbCompany.AdPassword = ApiCommon.Encrypt(company.AdPassword);
                }

                dbCompany.CompanyApplications.Clear();

                foreach (var app in company.Applications)
                {
                    if (app.IsIncluded)
                    {
                        DbCompanyApplication tmpDbCompanyApplication = new DbCompanyApplication()
                        {
                            ApplicationId = app.Id,
                            AppActive = true //ver
                        };

                        dbCompany.CompanyApplications.Add(tmpDbCompanyApplication);
                    }
                }

                ctx.SaveChanges();

                company.Id = dbCompany.Id;

                return company;
            }
        }

        public void DeleteCompany(int companyId)
        {
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                if (companyId != 0)
                {
                    // TODO analizar como borra la relación                
                    var dbCompany = ctx.DbCompanies.Where(c => c.Id == companyId).Single();

                    StringBuilder errorBuilder = new StringBuilder();

                    if (dbCompany.CompanyApplicationUsers.Count > 0)
                    {
                        errorBuilder.AppendLine("La marca tiene usuarios asociados, debe eliminarlos primero.");
                    }

                    if (dbCompany.CompanyApplications.Count > 0)
                    {
                        errorBuilder.AppendLine("La marca tiene aplicaciones relacionadas, debe eliminarlas primero.");
                    }

                    if (errorBuilder.Length > 0)
                    {
                        throw new Exception(errorBuilder.ToString());
                    }

                    ctx.DbCompanies.DeleteObject(dbCompany);

                    ctx.SaveChanges();
                }
            }
        }

        public void DeleteUser(string userName)
        {
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                // TODO analizar como borra la relación con operationrole y operationuser
                DbAppUser dbAppUser = ctx.DbAppUsers.Where(u => u.UserName == userName).SingleOrDefault();

                if (dbAppUser != null)
                {
                    foreach (var dbCAU in dbAppUser.CompanyApplicationUsers)
                    {
                        dbCAU.Roles.Clear();
                        dbCAU.Operations.Clear();
                    }

                    dbAppUser.CompanyApplicationUsers.Clear();

                    ctx.DbAppUsers.DeleteObject(dbAppUser);
                }

                ctx.SaveChanges();
            }
        }

        public void DeleteOperation(int operationId)
        {
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                // TODO analizar como borra la relación con operationrole y operationuser
                DbOperation dbOperation = ctx.DbOperations.Where(o => o.Id == operationId).Single();

                if (dbOperation.Roles.Count > 0)
                {
                    throw new Exception("La operación tiene roles asignados, debe eliminar la relación antes de eliminar la operación.");
                }

                ctx.DbOperations.DeleteObject(dbOperation);

                ctx.SaveChanges();
            }
        }

        public RoleIncluded GetRoleForApplicationById(int applicationId, int roleId)
        {
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                DbRole dbRole = ctx.DbRoles.Where(r => r.ApplicationId == applicationId && r.Id == roleId).Single();
                List<DbOperation> operationsForApp = ctx.DbOperations.Where(o => o.ApplicationId == applicationId).ToList();
                List<DbOperation> operationsInRole = dbRole.Operations.ToList();

                RoleIncluded roleIncluded = new RoleIncluded();
                roleIncluded.Id = dbRole.Id;
                roleIncluded.Name = dbRole.Name;
                roleIncluded.ApplicationId = dbRole.ApplicationId;

                roleIncluded.Operations.Clear();

                foreach (var operation in operationsForApp)
                {
                    roleIncluded.Operations.Add(new OperationIncluded()
                    {
                        Id = operation.Id,
                        Name = operation.Name,
                        Description = operation.Description,
                        IsIncluded = operationsInRole.Where(oin => oin.Id == operation.Id).Count() > 0
                    });
                }

                return roleIncluded;
            }
        }

        public List<OperationIncluded> GetOperationsForApplication(int applicationId)
        {
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                DbApplication application = ctx.DbApplications.Where(a => a.Id == applicationId).Single();
                return application.Operations.Select(o => new OperationIncluded()
                {
                    Id = o.Id,
                    Name = o.Name,
                    Description = o.Description,
                    IsIncluded = false
                }).ToList();
            }
        }

        public RoleIncluded SaveRole(RoleIncluded role)
        {
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                DbRole dbRole = ctx.DbRoles.Where(r => r.Id == role.Id && r.ApplicationId == role.ApplicationId).SingleOrDefault();
                if (dbRole == null)
                {
                    dbRole = new DbRole();
                    ctx.DbRoles.AddObject(dbRole);
                }

                dbRole.Name = role.Name;
                dbRole.ApplicationId = role.ApplicationId;
                dbRole.Operations.Clear();

                foreach (var operation in role.Operations)
                {
                    if (operation.IsIncluded)
                    {
                        dbRole.Operations.Add(ctx.DbOperations.Where(o => o.Id == operation.Id).Single());
                    }
                }

                ctx.SaveChanges();
                role.Id = dbRole.Id;

                return role;
            }
        }

        public OperationIncluded SaveOperation(OperationIncluded operation)
        {
            using (ApiAuthEntities ctx = new ApiAuthEntities())
            {
                DbOperation dbOperation = ctx.DbOperations.Where(o => o.Id == operation.Id).SingleOrDefault();
                if (dbOperation == null)
                {
                    dbOperation = new DbOperation();
                    ctx.DbOperations.AddObject(dbOperation);
                }

                dbOperation.ApplicationId = operation.ApplicationId;
                dbOperation.Name = operation.Name;
                dbOperation.Description = operation.Description;

                ctx.SaveChanges();

                operation.Id = dbOperation.Id;

                return operation;
            }
        }
    }
}
